package vip.liux.front.domain.models.payment;

import jakarta.annotation.Nullable;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import vip.liux.domain.entities.auditing.TCreationAuditedAggregateRoot;
import vip.liux.front.domain.shared.payment.IPaymentRequestProductExtraParameterConfiguration;
import vip.liux.front.domain.shared.payment.PaymentRequestCompletedEto;
import vip.liux.front.domain.shared.payment.PaymentRequestProductCompletedEto;
import vip.liux.front.domain.shared.payment.PaymentRequestState;

import java.math.BigDecimal;
import java.util.*;

@Getter
@Entity
@Table(name = "payment_request")
@EntityListeners(AuditingEntityListener.class)
public class PaymentRequest extends TCreationAuditedAggregateRoot<String, PaymentRequest> {

    @OneToMany(mappedBy = "paymentRequest", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private final Set<PaymentRequestProduct> products;

    /**
     * 支付请求状态
     *
     * @see PaymentRequestState
     */
    @Enumerated(EnumType.ORDINAL)
    private PaymentRequestState state;

    @Setter
    private String currency;

    @Setter
    private String gateway;

    private String failReason;

    public PaymentRequest() {
        super();
        this.products = new HashSet<>();
        this.state = PaymentRequestState.WAITING;
    }

    public PaymentRequest(String id) {
        super(id);
        this.products = new HashSet<>();
        this.state = PaymentRequestState.WAITING;
    }

    public PaymentRequestProduct addProduct(
            String code, String name, BigDecimal unitPrice, int count, @Nullable BigDecimal totalPrice,
            @Nullable Map<String, IPaymentRequestProductExtraParameterConfiguration> extraProperties
    ) {
        PaymentRequestProduct paymentRequestProduct = new PaymentRequestProduct(this.getId(), code, name, unitPrice, count, totalPrice);
        if (extraProperties != null) {
            for (Map.Entry<String, IPaymentRequestProductExtraParameterConfiguration> entry : extraProperties.entrySet()) {
                // todo SetProperty
                paymentRequestProduct.getExtraProperties().put(entry.getKey(), entry.getValue());
            }
        }
        this.products.add(paymentRequestProduct);
        return paymentRequestProduct;
    }

    public void complete(boolean triggerEvent) {
        if (this.state != PaymentRequestState.WAITING) {
            throw new RuntimeException(String.format("Can not complete a payment in '%s' state!", this.state));
        }
        this.state = PaymentRequestState.COMPLETED;
        if (triggerEvent) {
            registerEvent(this.getPaymentRequestCompletedEto());
        }
    }

    public void fail(@Nullable String reason) {
        if (this.state != PaymentRequestState.WAITING) {
            throw new RuntimeException(String.format("Can not fail a payment in '%s' state!", this.state));
        }
        this.state = PaymentRequestState.FAILED;
        this.failReason = reason;
    }

    private Object getPaymentRequestCompletedEto() {
        List<PaymentRequestProductCompletedEto> list = new ArrayList<>();

        for (PaymentRequestProduct paymentRequestProduct : this.products) {
            list.add(new PaymentRequestProductCompletedEto(
                    paymentRequestProduct.getCode(),
                    paymentRequestProduct.getName(),
                    paymentRequestProduct.getCount()
            ));
        }

        PaymentRequestCompletedEto paymentRequestCompletedEto = new PaymentRequestCompletedEto(this.getId(), this.gateway, this.currency, list);

        for (Map.Entry<String, Object> keyValuePair : this.getExtraProperties().entrySet()) {
            paymentRequestCompletedEto.getExtraProperties().put(keyValuePair.getKey(), keyValuePair.getValue().toString());
        }
        return paymentRequestCompletedEto;
    }
}
